#include <memory.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

#include "hexfile.h"

#define BUF_SIZE 128

#define START_CODE_OFFSET 0
#define START_CODE_SIZE 1
#define BYTE_COUNT_OFFSET 1
#define BYTE_COUNT_SIZE 2
#define ADDRESS_OFFSET 3
#define ADDRESS_SIZE 4
#define RECORD_TYPE_OFFSET 7
#define RECORD_TYPE_SIZE 2
#define DATA_OFFSET 9

// typedef struct memory_segment {
//  uint32_t addr;
//  uint8_t *data;
//  uint32_t len;
//  uint32_t cap;
//} segment_t;

// typedef struct memory_space {
//  uint32_t start_addr;
//  segment_t *segments[255];
//  uint32_t len;
//  uint32_t cap;
//} space_t;

// static space_t space = {0, {0}, 0, 255};

static uint8_t g_memory[65536] = {0};
static uint32_t g_start_addr = 0;

////这里假定文件中的地址必须是递增的
// void set_value(uint32_t addr, uint8_t value) {
//  segment_t *seg = NULL;
//  for (uint32_t i = 0; i < space.len; i++) {
//    if (addr == space.segments[i]->addr + space.segments[i]->len) {
//      seg = space.segments[i];
//      break;
//    }
//  }
//  if (seg == NULL) {
//    seg = (segment_t *)malloc(sizeof(segment_t));
//    memset(seg, 0, sizeof(segment_t));
//    //确保space.cap空间足够
//    if (space.len + 1 >= space.cap) {
//      printf("max segment number is 255\n");
//      exit(1);
//    }

//    space.segments[space.len++] = seg;
//    seg->data = (uint8_t *)malloc(1024);
//    seg->cap = 1024;
//  }
//  //确保segments.cap空间足够
//  if (seg->len + 1 >= seg->cap) {
//    uint8_t *tmp = seg->data;
//    seg->cap += 1024;
//    seg->data = (uint8_t *)malloc(seg->cap);
//    memcpy(seg->data, tmp, seg->len);
//  }
//  seg->data[seg->len++] = value;
//}

// uint8_t get_value(uint32_t addr) {
//  for (uint32_t i = 0; i < space.len; i++) {
//    segment_t *segment = space.segments[i];
//    if (addr - segment->addr < segment->len) {
//      return segment->data[addr - segment->addr];
//    }
//  }

//  return 0;
//}

// void set_start_addr(uint32_t base_addr) { space.start_addr = base_addr; }

static void set_start_addr(uint32_t start_addr) { g_start_addr = start_addr; }
static void set_value(uint32_t addr, uint8_t value) { g_memory[addr] = value; }
uint32_t get_start_addr() { return g_start_addr; }
uint8_t get_value(uint32_t addr) { return g_memory[addr]; }

static uint8_t c2b(char c) {
  if (c >= '0' && c <= '9')
    return (uint8_t)(c - '0');
  else if (c >= 'a' && c <= 'z')
    return (uint8_t)(c - 'a' + 10);
  else if (c >= 'A' && c <= 'Z')
    return (uint8_t)(c - 'A' + 10);
  printf("c2b 无效值\n");
  return 0;
}

static uint8_t read_uint8(char *buf, int offset) {
  return (c2b(buf[offset]) << 4 | c2b(buf[offset + 1])) & 0xff;
}

static uint16_t read_uint16(char *buf, int offset) {
  return (read_uint8(buf, offset) << 8 | read_uint8(buf, offset + 2)) & 0xffff;
}

static uint32_t read_uint32(char *buf, int offset) {
  return (uint32_t)(read_uint16(buf, offset) << 16 |
                    read_uint16(buf, offset + 4));
}

void load(const char *filename) {
  char buf[BUF_SIZE];
  FILE *file = fopen(filename, "rb");
  if (file == NULL) {
    printf("文件%s不存在\n", filename);
    return;
  }

  int line = 0;
  uint32_t h_addr = 0;

  while (fgets(buf, BUF_SIZE, file) != NULL) {
    line++;

    uint8_t byte_count = read_uint8(buf, BYTE_COUNT_OFFSET);
    uint16_t l_addr = read_uint16(buf, ADDRESS_OFFSET);
    uint8_t record_type = read_uint8(buf, RECORD_TYPE_OFFSET);

    //    printf("line: %d, record_type=%02x, addr=%04x, byte_count=%d\n", line,
    //           record_type, l_addr, byte_count);

    if (record_type == 0x00) {
      // Data
      for (uint8_t i = 0; i < byte_count; i++) {
        set_value(h_addr + l_addr + i, read_uint8(buf, DATA_OFFSET + i * 2));
      }
    } else if (record_type == 0x01) {
      // End of File
      // do nothing
      // printf("文件结束\n");
    } else if (record_type == 0x04) {
      // Extended Linear Address, 设置32位地址的高16位
      h_addr = (uint32_t)(read_uint16(buf, DATA_OFFSET) << 16);
      if (h_addr != 0) {
        printf("只支持16位地址\n");
        exit(1);
      }
    } else if (record_type == 0x05) {
      // Start Linear Address，设置初始地址
      set_start_addr(read_uint32(buf, DATA_OFFSET));
    } else {
      printf("Line %d, 未支持的record type: 0x%02x\n", line, record_type);
    }
  }

  fclose(file);
}
